T006 自定义控件 扫描雷达动画 发表于 2019-07-19 | 分类于 Custom View | 一、说明 表层是一张指针图,增加旋转动画,每旋转一周,增加一个波纹扩散动画效果。 波纹扩散动画效果是在底层的波纹图上实现缩放和透明度变化的组合动画。 使用容器来保存波纹扩散动画,便于复用。 二、完整代码123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179package com.xxt.xtest;import android.animation.Animator;import android.animation.AnimatorSet;import android.animation.ObjectAnimator;import android.animation.ValueAnimator;import android.content.Context;import android.util.AttributeSet;import android.view.Gravity;import android.view.LayoutInflater;import android.view.View;import android.view.animation.LinearInterpolator;import android.widget.FrameLayout;import android.widget.ImageView;import android.widget.TextView;import java.util.ArrayList;import java.util.List;public class ScanningView extends FrameLayout { /** * 指针 */ private ImageView ivNeedle; /** * 波纹 */ private ImageView ivRipple; /** * 中间文字 */ private TextView tvTitle; /** * 装波纹的容器 */ private FrameLayout fl_move_circle; /** * 匀速插值器 */ private LinearInterpolator interpolator = new LinearInterpolator(); /** * 准备动画 */ private AnimatorSet prepareAnim; /** * 指针旋转动画 */ private ObjectAnimator needleRotateAnim; private List<AnimatorSet> animList = new ArrayList<>(); private int animCount = 3; private int index = 0; public ScanningView(Context context) { super(context); initView(); } public ScanningView(Context context, AttributeSet attrs) { super(context, attrs); initView(); } private void initView(){ View v = LayoutInflater.from(getContext()).inflate(R.layout.rotate_view,null); ivNeedle = v.findViewById(R.id.iv_btn); ivRipple = v.findViewById(R.id.iv_out_circle); tvTitle = v.findViewById(R.id.tv_title); fl_move_circle = v.findViewById(R.id.fl_move_circle); addView(v, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); initAnim(); prepareAnim.start(); } /** * 初始化动画 * 准备动画 prepareAnim * 扫描动画 scanningAnim */ private void initAnim() { initPrepareAnim(); initScanningAnim(); } private void initPrepareAnim() { // 开始循环的放大缩小波纹 ObjectAnimator outCircleAlpha = ObjectAnimator.ofFloat(ivRipple, "alpha", 0.2f, 0.6f); outCircleAlpha.setDuration(1000); ObjectAnimator outCircleAnimX = ObjectAnimator.ofFloat(ivRipple, "scaleX", 1f, 1.18f, 1f); outCircleAnimX.setDuration(2000); outCircleAnimX.setRepeatCount(ValueAnimator.INFINITE); outCircleAnimX.setInterpolator(interpolator); ObjectAnimator outCircleAnimY = ObjectAnimator.ofFloat(ivRipple, "scaleY", 1f, 1.18f, 1f); outCircleAnimY.setDuration(2000); outCircleAnimY.setRepeatCount(ValueAnimator.INFINITE); outCircleAnimY.setInterpolator(interpolator); prepareAnim = new AnimatorSet(); prepareAnim.playTogether(outCircleAnimX, outCircleAnimY, outCircleAlpha); } private void initScanningAnim() { // 指针转动动画 needleRotateAnim = ObjectAnimator.ofFloat(ivNeedle, "rotation", 0f, 360f); needleRotateAnim.setDuration(1800); needleRotateAnim.setInterpolator(interpolator); needleRotateAnim.setRepeatCount(ValueAnimator.INFINITE); needleRotateAnim.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { playRippleAnim(); } @Override public void onAnimationEnd(Animator animation) {} @Override public void onAnimationCancel(Animator animation) {} @Override public void onAnimationRepeat(Animator animation) { playRippleAnim(); } }); } private void initRippleAnim() { final ImageView imageView = new ImageView(getContext()); LayoutParams lp = new LayoutParams(dip2px(getContext(), 110), dip2px(getContext(), 110)); lp.gravity = Gravity.CENTER; imageView.setLayoutParams(lp); imageView.setImageResource(R.drawable.ripple); fl_move_circle.addView(imageView); ObjectAnimator outCircleAnimX = ObjectAnimator.ofFloat(imageView, "scaleX", 1f, 5f); ObjectAnimator outCircleAnimY = ObjectAnimator.ofFloat(imageView, "scaleY", 1f, 5f); ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(imageView, "alpha", 0.6f, 0); outCircleAnimX.setDuration(5000); outCircleAnimY.setDuration(5000); alphaAnim.setDuration(5000); final AnimatorSet animatorSet = new AnimatorSet(); animatorSet.playTogether(outCircleAnimX, outCircleAnimY, alphaAnim); animList.add(animatorSet); } private void playRippleAnim() { if (animList.size() < animCount) { initRippleAnim(); } animList.get(index).start(); index++; if (index == animCount) index = 0; } /** * 模拟开始 */ public void onceClick(){ // 取消掉循环的波纹 prepareAnim.cancel(); ivRipple.setVisibility(GONE); needleRotateAnim.start(); tvTitle.setText("扫描中"); } /** * 根据手机的分辨率从 dip 的单位 转成为 px(像素) */ public static int dip2px(Context context, float dpValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); }} 其布局文件 res/layout/scanning_view.xml 如下:12345678910111213141516171819202122232425262728293031323334353637<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:background="#69C8FA" android:layout_height="match_parent"> <FrameLayout android:id="@+id/fl_move_circle" android:layout_width="match_parent" android:layout_height="match_parent"/> <ImageView android:id="@+id/iv_out_circle" android:layout_width="110dp" android:layout_height="110dp" android:layout_gravity="center" android:alpha="0.6" android:src="@drawable/ripple" /> <ImageView android:id="@+id/iv_btn" android:layout_width="110dp" android:layout_height="110dp" android:layout_gravity="center" android:src="@drawable/needle" /> <TextView android:id="@+id/tv_title" android:layout_width="wrap_content" android:textColor="#ffffff" android:layout_gravity="center" android:text="点击扫描" android:textSize="10sp" android:layout_marginTop="13dp" android:layout_height="wrap_content" /></FrameLayout> 三、使用1234567891011121314151617181920212223242526272829public class DemoActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.act_demo); final ScanningView view = findViewById(R.id.scanning); view.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { view.onceClick(); } }); }}<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <com.xxt.xtest.ScanningView android:id="@+id/scanning" android:layout_width="match_parent" android:layout_height="match_parent"/></LinearLayout>